<?php

/**
 * Created by JetBrains PhpStorm.
 * User: taoqili
 * Date: 12-7-18
 * Time: 上午11: 32
 * UEditor编辑器通用上传类
 */
namespace Admin\Util;
class Uploader
{
    private $file; //文件上传对象
    private $config = array(
        "maxSize" => 102400000,
        "allowFiles" => array(
            ".png", ".jpg", ".jpeg", ".gif", ".bmp",
            ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
            ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
            ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
            ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml",
        ),
    );
    private $oriName; //原始文件名
    private $newFilePath; //新文件的路径
    private $fileSize; //文件大小
    private $fileType; //文件类型
    private $stateInfo; //上传状态信息
    private $stateMap = array(
        "SUCCESS",
        "ERROR_TMP_FILE" => "临时文件错误",
        "ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件",
        "ERROR_SIZE_EXCEED" => "文件大小超出网站限制",
        "ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许",
        "ERROR_CREATE_DIR" => "目录创建失败",
        "ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限",
        "ERROR_FILE_MOVE" => "文件保存时出错",
        "ERROR_FILE_NOT_FOUND" => "找不到上传文件",
        "ERROR_WRITE_CONTENT" => "写入文件内容错误",
        "ERROR_UNKNOWN" => "未知错误",
        "ERROR_DEAD_LINK" => "链接不可用",
        "ERROR_HTTP_LINK" => "链接不是http链接",
        "ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确"
    );
    private $dir_prefix; //网站根目录的绝对路径
    private $oss_sdk_service;

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->dir_prefix = WEB_PATH . '/www';
        //上传到oss
        require_once APP_PATH . 'Admin/Util/oss/sdk.class.php';
        $this->oss_sdk_service = new \ALIOSS();
    }

    /**
     * 上传文件的主处理方法
     * @return mixed
     */
    public function upFile($fileField)
    {
        $file = $this->file = $_FILES[$fileField];
        if (!$file) {
            $this->stateInfo = $this->getStateInfo("ERROR_FILE_NOT_FOUND");
            return;
        }
        if ($this->file['error']) {
            $this->stateInfo = $this->getStateInfo($file['error']);
            return;
        } else if (!file_exists($file['tmp_name'])) {
            $this->stateInfo = $this->getStateInfo("ERROR_TMP_FILE_NOT_FOUND");
            return;
        } else if (!is_uploaded_file($file['tmp_name'])) {
            $this->stateInfo = $this->getStateInfo("ERROR_TMPFILE");
            return;
        }

        $this->oriName = $file['name'];
        $this->fileSize = $file['size'];
        $this->fileType = $this->getFileExt();
        $this->newFilePath = $this->getNewFilePath();
        $newFileFullPath = $this->dir_prefix . $this->newFilePath;
        $dirName = dirname($newFileFullPath);

        //检查文件大小是否超出限制
        if (!$this->checkSize()) {
            $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
            return;
        }

        //检查是否不允许的文件格式
        if (!$this->checkType()) {
            $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
            return;
        }

        //创建目录失败
        if (!file_exists($dirName) && !mkdir($dirName, 0777, true)) {
            $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
            return;
        } else if (!is_writeable($dirName)) {
            $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
            return;
        }

        //移动文件
        if (!(move_uploaded_file($file["tmp_name"], $newFileFullPath) && file_exists($newFileFullPath))) {
            $this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE");
        } else {
            $this->stateInfo = $this->stateMap[0];
            $this->oss_sdk_service->upload_file_by_file('manyu', 'caidan' . $this->newFilePath, $this->dir_prefix . $this->newFilePath);
        }
    }

    /**
     * 处理base64编码的图片上传
     * @return mixed
     */
    public function upBase64($fileField)
    {
        $base64Data = $_POST[$fileField];
        $img = base64_decode($base64Data);

        $this->oriName = 'Base64File';
        $this->fileSize = strlen($img);
        $this->file["tmp_name"] = $this->dir_prefix . '/' . uniqid(mt_rand(), true);
        file_put_contents($this->file["tmp_name"], $img);

        $this->fileType = $this->getFileExt();
        $this->newFilePath = $this->getNewFilePath();
        $newFileFullPath = $this->dir_prefix . $this->newFilePath;
        $dirName = dirname($newFileFullPath);

        //检查文件大小是否超出限制
        if (!$this->checkSize()) {
            $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
            return;
        }

        //创建目录失败
        if (!file_exists($dirName) && !mkdir($dirName, 0777, true)) {
            $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
            return;
        } else if (!is_writeable($dirName)) {
            $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
            return;
        }

        //移动文件
        if (!(rename($this->file["tmp_name"], $newFileFullPath) && file_exists($newFileFullPath))) { //移动失败
            $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
        } else { //移动成功
            $this->stateInfo = $this->stateMap[0];
            $this->oss_sdk_service->upload_file_by_file('manyu', 'caidan' . $this->newFilePath, $this->dir_prefix . $this->newFilePath);
        }
    }

    /**
     * 拉取远程图片
     * @return mixed
     */
    public function saveRemote($imgUrl)
    {
        $imgUrl = htmlspecialchars($imgUrl);
        $imgUrl = str_replace("&amp;", "&", $imgUrl);

        //http开头验证
        if (strpos($imgUrl, "http") !== 0) {
            $this->stateInfo = $this->getStateInfo("ERROR_HTTP_LINK");
            return;
        }

        //获取远程图片
        $timeout = 5;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $imgUrl);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        $img = curl_exec($ch);
        curl_close($ch);
        $this->file["tmp_name"] = $this->dir_prefix . '/' . uniqid(mt_rand(), true);
        file_put_contents($this->file["tmp_name"], $img);

        $this->oriName = "remoteFile";
        $this->fileSize = strlen($img);
        $this->fileType = $this->getFileExt();
        $this->newFilePath = $this->getNewFilePath($imgUrl);
        $newFileFullPath = $this->dir_prefix . $this->newFilePath;
        $dirName = dirname($newFileFullPath);

        //抓取到相同的url图片，查看是否已经抓取过，已经抓取的不重复抓取
        if (file_exists($newFileFullPath)) {
            unlink($this->file["tmp_name"]);
            $this->stateInfo = $this->stateMap[0];
            return;
        }

        //检查文件大小是否超出限制
        if (!$this->checkSize()) {
            $this->stateInfo = $this->getStateInfo("ERROR_SIZE_EXCEED");
            return;
        }

        //创建目录失败
        if (!file_exists($dirName) && !mkdir($dirName, 0777, true)) {
            $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
            return;
        } else if (!is_writeable($dirName)) {
            $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
            return;
        }

        //移动文件
        if (!(rename($this->file["tmp_name"], $newFileFullPath) && file_exists($newFileFullPath))) { //移动失败
            $this->stateInfo = $this->getStateInfo("ERROR_WRITE_CONTENT");
        } else { //移动成功
            $this->stateInfo = $this->stateMap[0];
            $this->oss_sdk_service->upload_file_by_file('manyu', 'caidan' . $this->newFilePath, $this->dir_prefix . $this->newFilePath);
        }
    }

    /**
     * 上传错误检查
     * @param $errCode
     * @return string
     */
    private function getStateInfo($errCode)
    {
        return !$this->stateMap[$errCode] ? $this->stateMap["ERROR_UNKNOWN"] : $this->stateMap[$errCode];
    }

    /**
     * 获取文件扩展名
     * @return string
     */
    private function getFileExt()
    {
        if (!empty($this->file['tmp_ext'])) {
            return $this->file['tmp_ext'];
        }
        $file = fopen($this->file['tmp_name'], "rb");
        $bin = fread($file, 2);
        fclose($file);
        $strInfo = @unpack("c2chars", $bin);
        $typeCode = (string)$strInfo['chars1'] . (string)$strInfo['chars2'];
        $typeArr = array(
            '-1-40' => '.jpg', '255216' => '.jpg', '-11980' => '.png', '13780' => '.png',
            '7790' => '.exe', '7784' => '.midi', '8297' => '.rar', '8075' => '.rar',
            '7173' => '.gif', '6677' => '.bmp', '7368' => '.mp3', '-1-5' => '.mp3',
            '-1-6' => '.mp3', '00' => '.mp4', '8273' => '.webp',
        );
        $fileExt = empty($typeArr[$typeCode]) ? ".unknown{$typeCode}" : $typeArr[$typeCode];
        $this->file['tmp_ext'] = $fileExt;
        return $fileExt;
    }

    /**
     * 重命名文件
     * @return string
     */
    private function getNewFilePath($imgUrl = '')
    {
        $ymd = date('Ymd');
        $format = uniqid("/uploads/image/{$ymd}/", true);
        $format = str_replace('.', '', $format);
        if ($imgUrl) {
            $format = "/uploads/image/{$ymd}/" . md5($imgUrl);
        }

        //给图片添加高和宽
        $ext = $this->getFileExt();
        if (in_array($ext, array('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'))) {
            $magic = new \Imagick();
            $magic->readImage($this->file["tmp_name"]);

            //原图是webp，转成jpeg
            if ($magic->getImageFormat() == 'WEBP') {
                $magic->setImageFormat('jpeg');
                $magic->writeImages($this->file["tmp_name"], true);
            }

            //gif只取第一帧
            if ($magic->getImageFormat() == 'GIF') {
                $magic = $magic->getImage();
            }
            $srcWidth = round($magic->getImageWidth());
            $srcHeight = round($magic->getImageHeight());
            if (!empty($srcWidth)) {
                $format .= ".{$srcWidth}x{$srcHeight}";
            }
        }

        return $format . $ext;
    }

    /**
     * 文件类型检测
     * @return bool
     */
    private function checkType()
    {
        return in_array($this->getFileExt(), $this->config["allowFiles"]);
    }

    /**
     * 文件大小检测
     * @return bool
     */
    private function checkSize()
    {
        return $this->fileSize <= ($this->config["maxSize"]);
    }

    /**
     * 获取当前上传成功文件的各项信息
     * @return array
     */
    public function getFileInfo()
    {
        return array(
            "state" => $this->stateInfo,
            "url" => $this->newFilePath,
            "title" => basename($this->newFilePath),
            "original" => $this->oriName,
            "type" => $this->fileType,
            "size" => $this->fileSize
        );
    }

    /**
     * +----------------------------------------------------------
     * 用imagick库生成缩略图
     * +----------------------------------------------------------
     * @static
     * @access public
     * +----------------------------------------------------------
     * @param string $image 原图
     * @param string $maxWidth 宽度
     * @param string $maxHeight 高度
     * @param string $cut 缩略类型，fixed是固定宽高，ratable是固定比例且不能超出宽或者高
     * +----------------------------------------------------------
     * @return string
     * +----------------------------------------------------------
     */
    public function imkThumb($image, $maxWidth = 200, $maxHeight = 50, $cut = 'fixed')
    {
        if (empty($image)) {
            return '';
        }
        $magic = new \Imagick();
        $magic->readImage($this->dir_prefix . $image);
        $srcWidth = $magic->getImageWidth();
        $srcHeight = $magic->getImageHeight();
        //gif只取第一帧
        if ($magic->getImageFormat() == 'GIF') {
            $magic = $magic->getImage();
        }
        //png需要加白色背景
        if ($magic->getImageFormat() == 'PNG') {
            $canvas = new \Imagick();
            $canvas->newImage($srcWidth, $srcHeight, '', 'jpeg');
            $canvas->compositeImage($magic, \Imagick::COMPOSITE_OVER, 0, 0);
            $magic = $canvas;
        }

        if ($cut == 'fixed') {
            //宽高不能超过5000，否则内存溢出
            $width = min($maxWidth, 5000);
            $height = min($maxHeight, 5000);
            $magic->cropThumbnailImage($width, $height);
        } else {
            $maxWidth = !$maxWidth ? 99999 : $maxWidth;
            $maxHeight = !$maxHeight ? 99999 : $maxHeight;
            // 计算缩放比例
            $scale = min($maxWidth / $srcWidth, $maxHeight / $srcHeight);
            $width = round($srcWidth * $scale);
            $height = round($srcHeight * $scale);
            $magic->resizeImage($width, $height, \Imagick::FILTER_CATROM, 1, true);
        }

        $magic->setImageCompression(\Imagick::COMPRESSION_JPEG);
        $magic->setImageCompressionQuality(90);
        $magic->stripImage();
        //生成jpg
        $thumbName = $image . ".{$width}x{$height}.jpg";
        if (!file_exists($this->dir_prefix . $thumbName)) {
            $magic->setImageFormat('jpeg');
            $magic->writeImages($this->dir_prefix . $thumbName, true);
            $this->oss_sdk_service->upload_file_by_file('manyu', 'caidan' . $thumbName, $this->dir_prefix . $thumbName);
        }
        //生成webp
        $thumbWebp = $image . ".{$width}x{$height}.webp";
        if (!file_exists($this->dir_prefix . $thumbWebp)) {
            $magic->setImageFormat('webp');
            $magic->writeImages($this->dir_prefix . $thumbWebp, true);
            $this->oss_sdk_service->upload_file_by_file('manyu', 'caidan' . $thumbWebp, $this->dir_prefix . $thumbWebp);
        }
        $magic->clear();
        $magic->destroy();

        return $thumbName;
    }

    // 暂时弃用
    public function gdThumb($none, $maxWidth = 200, $maxHeight = 50, $cut = 'fixed')
    {
        // 获取原图信息
        $newFileFullPath = $this->dir_prefix . $this->newFilePath;
        $imageInfo = getimagesize($newFileFullPath);
        if ($imageInfo === false) {
            return false;
        }
        $srcWidth = abs($imageInfo[0]);
        $srcHeight = abs($imageInfo[1]);
        $type = strtolower(substr(image_type_to_extension($imageInfo[2]), 1));

        $sw = 0;
        $sh = 0;
        if ($cut == 'ratable') {
            $maxWidth = !$maxWidth ? 99999 : $maxWidth;
            $maxHeight = !$maxHeight ? 99999 : $maxHeight;
            // 计算缩放比例
            $scale = min($maxWidth / $srcWidth, $maxHeight / $srcHeight);
            $width = round($srcWidth * $scale);
            $height = round($srcHeight * $scale);
        } else {
            //宽高不能超过5000，否则内存溢出
            $width = min($maxWidth, 5000);
            $height = min($maxHeight, 5000);

            $ow = $srcWidth;
            $oh = $srcHeight;
            $k1 = $height / $width;
            $k2 = $oh / $ow;
            if ($k1 < $k2) {
                $srcWidth = $ow;
                $srcHeight = $ow * $k1;
                $sw = 0;
                $sh = ($oh - $srcHeight) / 2;
            } else {
                $srcWidth = $oh / $k1;
                $srcHeight = $oh;
                $sw = ($ow - $srcWidth) / 2;
                $sh = 0;
            }
        }

        // 载入原图
        $createFun = 'ImageCreateFrom' . ($type == 'jpg' ? 'jpeg' : $type);
        //内存限制调到200M，最大可传4M的图片
        if (function_exists('ini_set')) ini_set('memory_limit', '200M');
        $srcImg = $createFun($newFileFullPath);

        //创建缩略图
        if ($type != 'gif')
            $thumbImg = imagecreatetruecolor($width, $height);
        else
            $thumbImg = imagecreate($width, $height);

        // 复制图片
        imagecopyresampled($thumbImg, $srcImg, 0, 0, $sw, $sh, $width, $height, $srcWidth, $srcHeight);
        if ('gif' == $type || 'png' == $type) {
            $background_color = imagecolorallocate($thumbImg, 0, 255, 0); //  指派一个绿色
            imagecolortransparent($thumbImg, $background_color); //  设置为透明色，若注释掉该行则输出绿色的图
        }

        // 对jpeg图形设置隔行扫描
        if ('jpg' == $type || 'jpeg' == $type)
            imageinterlace($thumbImg, 1);
        $thumbName = $this->newFilePath . ".{$width}x{$height}.jpg";
        //$thumbWebp = $this->newFilePath . ".{$width}x{$height}.webp";
        imagejpeg($thumbImg, $this->dir_prefix . $thumbName, 90);
        //imagewebp($thumbImg, $this->dir_prefix . $thumbWebp);
        imagedestroy($thumbImg);
        imagedestroy($srcImg);

        //上传到阿里云
        $this->oss_sdk_service->upload_file_by_file('manyu', 'caidan' . $thumbName, $this->dir_prefix . $thumbName);
        //$this->oss_sdk_service->upload_file_by_file('manyu', 'caidan' . $thumbWebp, $this->dir_prefix . $thumbWebp);
        return $thumbName;
    }
}